﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.ComponentModel;
using System.Runtime.InteropServices;

namespace Game
{
    class WinAPI
    {
        public enum ShowWindowCommand : int
        {
            Hide = 0,
            Normal = 1,
            ShowMinimized = 2,
            Maximize = 3, // is this the right value?     
            ShowMaximized = 3,
            ShowNoActivate = 4,
            Show = 5,
            Minimize = 6,
            ShowMinNoActive = 7,
            ShowNA = 8,
            Restore = 9,
            ShowDefault = 10,
            ForceMinimize = 11
        }

        public enum MessageCode : int
        {
            Create = 1,
            Destroy = 2,
            Show = 5,
            SetFocus = 7,
            KillFocus = 8,
            KeyDown = 256,
            KeyUp = 257,
            MouseLeftDown = 0x0201,
            MouseLeftUp = 0x0202,
            MouseRightDown = 0x0204,
            MouseRightUp = 0x0205
        }

        public enum MessageRemove
        {
            No = 0,
            Yes = 1
        }

        public struct Point
        {
            public int x;
            public int y;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct Message
        {
            public IntPtr handle;
            public uint msg;
            public IntPtr wParam;
            public IntPtr lParam;
            public uint time;
            public Point p;
        }

        [DllImport("user32.dll")]
        public static extern bool ShowWindow(IntPtr hWnd, ShowWindowCommand nCmdShow);
        [DllImport("user32.dll")]
        public static extern bool UpdateWindow(IntPtr hWnd);

        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool PeekMessage(out Message lpMsg, HandleRef hWnd, uint MsgFilterMin, uint wMsgFilterMax, uint wRemoveMsg);

        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool TranslateMessage(ref Message lpMsg);

        [DllImport("user32.dll")]
        public static extern IntPtr DispatchMessage(ref Message lpMsg);

        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool GetCursorPos(out Point lpPoint);

        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool SetCursorPos(int x, int y);

    }


    class Application : NativeWindow
    {
        private bool mActive = true;
        private bool mAlive = false;
        private IDK.Vector2 mMouseMove = new IDK.Vector2();
        private CreateParams mWindowParam = new CreateParams();

        public CreateParams WindowParam
        {
            get { return mWindowParam; }
        }

        public void Terminated()
        {
            mAlive = false;
        }

        public bool IsMouseMove()
        {
            return mMouseMove.x != 0 || mMouseMove.y != 0;
        }

        [System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust")]
        protected override void WndProc(ref Message msg)
        {
            switch ((WinAPI.MessageCode)msg.Msg)
            {
             case WinAPI.MessageCode.MouseLeftDown:
                    OnKeyDown(GenerateMouseControlInput(0, Events.ControlInput.Key.MouseLeft));
                    break;

                case WinAPI.MessageCode.MouseRightDown:
                    OnKeyDown(GenerateMouseControlInput(0, Events.ControlInput.Key.MouseRight));
                    break;
                    
                case WinAPI.MessageCode.MouseLeftUp:
                     OnKeyUp(GenerateControlInput(0, Events.ControlInput.Key.MouseLeft));
                    break;

                case WinAPI.MessageCode.MouseRightUp:
                    OnKeyUp(GenerateControlInput(0, Events.ControlInput.Key.MouseRight));
                    break;
  
                case WinAPI.MessageCode.Destroy:
                    mAlive = false;
                    break;
                case WinAPI.MessageCode.SetFocus:
                    if (mAlive)
                    {
                        Active(true);
                    }
                    break;
                case WinAPI.MessageCode.KillFocus:
                    if (mAlive)
                    {
                        Active(false);
                    }
                    break;
                case WinAPI.MessageCode.KeyDown:
                    OnKeyDown(GenerateControlInput(0, (Events.ControlInput.Key)(msg.WParam.ToInt32())));
                    break;
                case WinAPI.MessageCode.KeyUp:
                    OnKeyUp(GenerateControlInput(0, (Events.ControlInput.Key)(msg.WParam.ToInt32())));
                    break;
            }
            base.WndProc(ref msg);
        }


        protected Events.MouseControlInput GenerateMouseControlInput(uint state, Events.ControlInput.Key key)
        {
            Set<Events.ControlInput.State> kstate = new Set<Events.ControlInput.State>(state);
            return new Events.MouseControlInput(mMouseMove.x, mMouseMove.y, key, kstate);
        }

        protected Events.ControlInput GenerateControlInput(uint state, Events.ControlInput.Key key)
        {
            Set<Events.ControlInput.State> kstate = new Set<Events.ControlInput.State>(state);
            return new Events.ControlInput(key, kstate);
        }

        public void Active(bool active)
        {
            Log.Hint(String.Format("Aplication Active = {0}", active));
            mActive = active;
            OnActive(active);
        }
        public Application()
        {
            Log.Hint(String.Format("Aplication creating.."));
            const int WS_VISIBLE = 0x10000000;

            mWindowParam.Caption = "Application";
            mWindowParam.Width = 1024;
            mWindowParam.Height = 768;
            //setcursor
            WinAPI.SetCursorPos(mWindowParam.Width / 2, mWindowParam.Height / 2);

            mWindowParam.X = 0;
            mWindowParam.Y = 0;
            mWindowParam.Style = WS_VISIBLE;
            mWindowParam.Parent = IntPtr.Zero;
            this.CreateHandle(mWindowParam);
            Log.Hint(String.Format("Create window"));
        }

        public void Update(float dt)
        {
            WinAPI.Point pos;
            WinAPI.GetCursorPos(out pos);
            mMouseMove.x = mWindowParam.Width / 2 - pos.x;
            mMouseMove.y = mWindowParam.Height / 2 - pos.y;
            
            //Set Cursor Position
            WinAPI.SetCursorPos(mWindowParam.Width / 2, mWindowParam.Height / 2);

            if (IsMouseMove())
            {
                OnMouseMove(mMouseMove);
            }


            if (OnUpdate(dt) == false)
            {
                mAlive = false;
            }
        }

        public void Run()
        {
            Log.Hint(String.Format("Create run"));
            WinAPI.ShowWindow(Handle, WinAPI.ShowWindowCommand.Show);
            WinAPI.UpdateWindow(Handle);
            OnCreate();

            HandleRef hWnd = new HandleRef(this, Handle);

            mAlive = true;

            IDK.Timer timer = new IDK.Timer();
            WinAPI.Message msg;
            while (mAlive)
            {
                while (WinAPI.PeekMessage(out msg, hWnd, 0, 0, (int)WinAPI.MessageRemove.Yes))
                {
                    WinAPI.TranslateMessage(ref msg);
                    WinAPI.DispatchMessage(ref msg);
                }

                //Console.WriteLine(timer.GetMicroseconds());
                if (mActive)
                {
                    Update(timer.GetMicroseconds());
                }
            }

            OnDestory();
        }

        protected virtual void OnCreate()
        {
        }

        protected virtual bool OnUpdate(float dt)
        {
            return true;
        }

        protected virtual void OnDestory()
        {
            ReleaseHandle();
        }

        protected virtual void OnKeyDown(Events.ControlInput input)
        {
        }

        protected virtual void OnKeyUp(Events.ControlInput input)
        {
        }

        protected virtual void OnMouseMove(IDK.Vector2 xyMove)
        {
        }

        protected virtual void OnActive(bool value)
        {
        }
    }
}
